module mem(
    input [63:0] mem_data_i,
    input [3:0] mem_control,
    input [63:0] mem_addr,
    input [63:0] i_badvaddr,
    input [63:0] i_excode,
    input i_except_ena,
    input data_w_valid,
    input data_r_valid,
    input [63:0] mem_wdata,

    output reg [63:0] mem_data_o,
    output reg [7:0] mem_mask,
    output data_r_ena,
    output data_w_ena,
    output [63:0] data_w_addr,
    output [63:0] o_badvaddr,
    output [63:0] o_excode,
    output o_except_ena,
    output stall_o,
    output [63:0] data_w_data,
    output [63:0] data_r_addr
);

    always @(mem_control) begin
        case (mem_control)
            4'b0001 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_data_o = {{56{mem_data_i[7]}}, mem_data_i[7:0]};
                    end
                    3'b001 : begin
                        mem_data_o = {{56{mem_data_i[15]}}, mem_data_i[15:8]};
                    end
                    3'b010 : begin
                        mem_data_o = {{56{mem_data_i[23]}}, mem_data_i[23:16]};
                    end
                    3'b011 : begin
                        mem_data_o = {{56{mem_data_i[31]}}, mem_data_i[31:24]};
                    end
                    3'b100 : begin
                        mem_data_o = {{56{mem_data_i[39]}}, mem_data_i[39:32]};
                    end
                    3'b101 : begin
                        mem_data_o = {{56{mem_data_i[47]}}, mem_data_i[47:40]};
                    end
                    3'b110 : begin
                        mem_data_o = {{56{mem_data_i[55]}}, mem_data_i[55:48]};
                    end
                    3'b111 : begin
                        mem_data_o = {{56{mem_data_i[63]}}, mem_data_i[63:56]};
                    end
                    default : begin
                        mem_data_o = 64'd0;
                    end
                endcase
            end
            4'b0010 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_data_o = {{48{mem_data_i[15]}}, mem_data_i[15:0]};
                    end
                    3'b010 : begin
                        mem_data_o = {{48{mem_data_i[31]}}, mem_data_i[31:16]};
                    end
                    3'b100 : begin
                        mem_data_o = {{48{mem_data_i[47]}}, mem_data_i[47:32]};
                    end
                    3'b110 : begin
                        mem_data_o = {{48{mem_data_i[63]}}, mem_data_i[63:48]};
                    end
                    default : begin
                        mem_data_o = 64'd0;
                    end
                endcase
            end
            4'b0011 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_data_o = {56'd0, mem_data_i[7:0]};
                    end
                    3'b001 : begin
                        mem_data_o = {56'd0, mem_data_i[15:8]};
                    end
                    3'b010 : begin
                        mem_data_o = {56'd0, mem_data_i[23:16]};
                    end
                    3'b011 : begin
                        mem_data_o = {56'd0, mem_data_i[31:24]};
                    end
                    3'b100 : begin
                        mem_data_o = {56'd0, mem_data_i[39:32]};
                    end
                    3'b101 : begin
                        mem_data_o = {56'd0, mem_data_i[47:40]};
                    end
                    3'b110 : begin
                        mem_data_o = {56'd0, mem_data_i[55:48]};
                    end
                    3'b111 : begin
                        mem_data_o = {56'd0, mem_data_i[63:56]};
                    end
                    default : begin
                        mem_data_o = 64'd0;
                    end
                endcase
            end
            4'b0100 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_data_o = {48'd0, mem_data_i[15:0]};
                    end
                    3'b010 : begin
                        mem_data_o = {48'd0, mem_data_i[31:16]};
                    end
                    3'b100 : begin
                        mem_data_o = {48'd0, mem_data_i[47:32]};
                    end
                    3'b110 : begin
                        mem_data_o = {48'd0, mem_data_i[63:48]};
                    end
                    default : begin
                        mem_mask = 8'b00000000;
                    end
                endcase
            end
            4'b0101 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_data_o = {{32{mem_data_i[31]}}, mem_data_i[31:0]};
                    end
                    3'b100 : begin
                        mem_data_o = {{32{mem_data_i[63]}}, mem_data_i[63:32]};
                    end
                    default : begin
                        mem_data_o = 64'd0;
                    end
                endcase
            end
            4'b0110 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_data_o = {32'd0, mem_data_i[31:0]};
                    end
                    3'b100 : begin
                        mem_data_o = {32'd0, mem_data_i[63:32]};
                    end
                    default : begin
                        mem_data_o = 64'd0;
                    end
                endcase
            end
            4'b0111 : begin
                mem_data_o = mem_data_i;
            end
            4'b1000 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_mask = 8'b00000001;
                        //data_w_data = {mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0]};
                    end
                    3'b001 : begin
                        mem_mask = 8'b00000010;
                        //data_w_data = {mem_wdata[15:8], mem_wdata[15:8], mem_wdata[15:8], mem_wdata[15:8], mem_wdata[15:8], mem_wdata[15:8], mem_wdata[15:8], mem_wdata[15:8]};
                    end
                    3'b010 : begin
                        mem_mask = 8'b00000100;
                        //data_w_data = {mem_wdata[23:16], mem_wdata[23:16], mem_wdata[23:16], mem_wdata[23:16], mem_wdata[23:16], mem_wdata[23:16], mem_wdata[23:16], mem_wdata[23:16]};
                    end
                    3'b011 : begin
                        mem_mask = 8'b00001000;
                        //data_w_data = {mem_wdata[31:24], mem_wdata[31:24], mem_wdata[31:24], mem_wdata[31:24], mem_wdata[31:24], mem_wdata[31:24], mem_wdata[31:24], mem_wdata[31:24]};
                    end
                    3'b100 : begin
                        mem_mask = 8'b00010000;
                        //data_w_data = {mem_wdata[39:32], mem_wdata[39:32], mem_wdata[39:32], mem_wdata[39:32], mem_wdata[39:32], mem_wdata[39:32], mem_wdata[39:32], mem_wdata[39:32]};
                    end
                    3'b101 : begin
                        mem_mask = 8'b00100000;
                        //data_w_data = {mem_wdata[47:40], mem_wdata[47:40], mem_wdata[47:40], mem_wdata[47:40], mem_wdata[47:40], mem_wdata[47:40], mem_wdata[47:40], mem_wdata[47:40]};
                    end
                    3'b110 : begin
                        mem_mask = 8'b01000000;
                        //data_w_data = {mem_wdata[55:48], mem_wdata[55:48], mem_wdata[55:48], mem_wdata[55:48], mem_wdata[55:48], mem_wdata[55:48], mem_wdata[55:48], mem_wdata[55:48]};
                    end
                    3'b111 : begin
                        mem_mask = 8'b10000000;
                        //data_w_data = {mem_wdata[63:56], mem_wdata[63:56], mem_wdata[63:56], mem_wdata[63:56], mem_wdata[63:56], mem_wdata[63:56], mem_wdata[63:56], mem_wdata[63:56]};
                    end
                    default : begin
                        mem_mask = 8'b00000000;
                    end
                endcase
            end
            4'b1001 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_mask = 8'b00000011;
                        //data_w_data = {mem_wdata[15:0], mem_wdata[15:0], mem_wdata[15:0], mem_wdata[15:0]};
                    end
                    3'b010 : begin
                        mem_mask = 8'b00001100;
                        //data_w_data = {mem_wdata[31:16], mem_wdata[31:16], mem_wdata[31:16], mem_wdata[31:16]};
                    end
                    3'b100 : begin
                        mem_mask = 8'b00110000;
                        //data_w_data = {mem_wdata[47:32], mem_wdata[47:32], mem_wdata[47:32], mem_wdata[47:32]};
                    end
                    3'b110 : begin
                        mem_mask = 8'b11000000;
                        //data_w_data = {mem_wdata[63:48], mem_wdata[63:48], mem_wdata[63:48], mem_wdata[63:48]};
                    end
                    default : begin
                        mem_mask = 8'b00000000;
                    end
                endcase
            end
            4'b1010 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_mask = 8'b00001111;
                        //data_w_data = {mem_wdata[31:0], mem_wdata[31:0]};
                    end
                    3'b100 : begin
                        mem_mask = 8'b11110000;
                        //data_w_data = {mem_wdata[63:32], mem_wdata[63:32]};
                    end
                    default : begin
                        mem_mask = 8'b00000000;
                    end
                endcase
            end
            4'b1011 : begin
                mem_mask = 8'b11111111;
                //data_w_data = mem_wdata[63:0];
            end
            default : begin
                mem_mask = 8'b00000000;
                mem_data_o = 64'd0;
            end
        endcase
    end    

    assign data_w_addr = mem_addr;
    assign data_r_addr = mem_addr;
    assign data_w_data = mem_control == 4'b1000 ? {mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0], mem_wdata[7:0]} : (mem_control == 4'b1001 ? {mem_wdata[15:0], mem_wdata[15:0], mem_wdata[15:0], mem_wdata[15:0]} : (mem_control == 4'b1010 ? {mem_wdata[31:0], mem_wdata[31:0]} : (mem_control == 4'b1011 ? mem_wdata : 64'd0)));
    assign stall_o = (~data_r_valid && (mem_control == 4'b0001 | mem_control == 4'b0010 | mem_control == 4'b0011 | mem_control == 4'b0100 | mem_control == 4'b0101 | mem_control == 4'b0110 | mem_control == 4'b0111)) || (~data_w_valid && (mem_control == 4'b1000 | mem_control == 4'b1001 | mem_control == 4'b1010 | mem_control == 4'b1011));
    assign data_r_ena = ~data_r_valid && (mem_control == 4'b0001 | mem_control == 4'b0010 | mem_control == 4'b0011 | mem_control == 4'b0100 | mem_control == 4'b0101 | mem_control == 4'b0110 | mem_control == 4'b0111);
    assign data_w_ena = ~data_w_valid && (mem_control == 4'b1000 | mem_control == 4'b1001 | mem_control == 4'b1010 | mem_control == 4'b1011);
    //assign control = (mem_control == 4'b0000 | mem_control == 4'b0001 | mem_control == 4'b0010 | mem_control == 4'b0011 | mem_control == 4'b0100 | mem_control == 4'b0101 | mem_control == 4'b0110) ? 2'b01 : (mem_control == 4'b0111 | mem_control == 4'b1000 | mem_control == 4'b1001 | mem_control == 4'b1010 ? 2'b10 : 2'b00); 
    assign o_except_ena = i_except_ena == 1'b1 ? 1'b1 : (((mem_control == 4'b0001 || mem_control == 4'b0011) && mem_addr[0] != 1'b0) || ((mem_control == 4'b0100 || mem_control == 4'b0101) && mem_addr[1:0] == 2'b00) || (mem_control == 4'b0110 && mem_addr[2:0] == 3'b000) || ((mem_control == 4'b1000 && mem_addr[0] == 1'b0) || (mem_control == 4'b1001 && mem_addr[1:0] == 2'b00) || (mem_control == 4'b1010 && mem_addr[2:0] == 3'b000)));
    assign o_excode = i_except_ena ? i_excode : (((mem_control == 4'b0001 || mem_control == 4'b0011) && mem_addr[0] != 1'b0) || ((mem_control == 4'b0100 || mem_control == 4'b0101) && mem_addr[1:0] == 2'b00) || (mem_control == 4'b0110 && mem_addr[2:0] == 3'b000) ? {1'b0, 63'd4} : ((mem_control == 4'b1000 && mem_addr[0] == 1'b0) || (mem_control == 4'b1001 && mem_addr[1:0] == 2'b00) || (mem_control == 4'b1010 && mem_addr[2:0] == 3'b000) ? {1'b0, 63'd6} : 64'd0));
endmodule
